////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Filename:    HoneyToTheBeeGuts.cpp                                        //
//  Developer:   Tommy Riddle                                                 //
//  Create date: 02/17/2006                                                   //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Description: This is the class implementation for the slot gut of the     //
//               Australian Slot machine called Honey to the Bee.             //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//        Copyright (c) 2006 - Realtime Gaming.  All rights reserved.         //
////////////////////////////////////////////////////////////////////////////////

#include "HoneyToTheBeeGuts.h"

////////////////////////////////////////////////////////////////////////////
/// HoneyToTheBeeGuts::m_aLineWinSpinResults
///
/// This array is used to quickly get the SpinResult for the number of
/// icons.  It is indexed by wheel icon and then count.  So to get the
/// SpinResult for 5 Aces, use the following code snippet:
///
///		SpinResult result = m_aLineWinSpinResults[ ICON_ACE ][ 5 ];
///
/// In this example result would be set to EVT_FIVE_ACE.
////////////////////////////////////////////////////////////////////////////
HoneyToTheBeeGuts::SpinResult HoneyToTheBeeGuts::m_aLineWinSpinResults[][6] = 
{
// Number of icons:			0			1					2						3						4						5
/* ICON_NINE */			{	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_THREE_NINE,			EVT_FOUR_NINE,			EVT_FIVE_NINE			},
/* ICON_TEN */			{	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_THREE_TEN,			EVT_FOUR_TEN,			EVT_FIVE_TEN			},
/* ICON_JACK */			{	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_THREE_JACK,			EVT_FOUR_JACK,			EVT_FIVE_JACK			},
/* ICON_QUEEN */		{	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_THREE_QUEEN,		EVT_FOUR_QUEEN,			EVT_FIVE_QUEEN			},
/* ICON_KING */			{	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_THREE_KING,			EVT_FOUR_KING,			EVT_FIVE_KING			},
/* ICON_ACE */			{	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_THREE_ACE,			EVT_FOUR_ACE,			EVT_FIVE_ACE			},
/* ICON_LADY_BEETLE */	{	EVT_LOSER,	EVT_LOSER,			EVT_TWO_LADY_BEETLE,	EVT_THREE_LADY_BEETLE,	EVT_FOUR_LADY_BEETLE,	EVT_FIVE_LADY_BEETLE	},
/* ICON_SUNFLOWER */	{	EVT_LOSER,	EVT_LOSER,			EVT_TWO_SUNFLOWER,		EVT_THREE_SUNFLOWER,	EVT_FOUR_SUNFLOWER,		EVT_FIVE_SUNFLOWER		},
/* ICON_HONEY_JAR */	{	EVT_LOSER,	EVT_LOSER,			EVT_TWO_HONEY_JAR,		EVT_THREE_HONEY_JAR,	EVT_FOUR_HONEY_JAR,		EVT_FIVE_HONEY_JAR		},
/* ICON_QUEEN_BEE */	{	EVT_LOSER,	EVT_LOSER,			EVT_TWO_QUEEN_BEE,		EVT_THREE_QUEEN_BEE,	EVT_FOUR_QUEEN_BEE,		EVT_FIVE_QUEEN_BEE		},
/* ICON_WORKER_BEE */	{	EVT_LOSER,	EVT_ONE_WORKER_BEE,	EVT_TWO_WORKER_BEE,		EVT_THREE_WORKER_BEE,	EVT_FOUR_WORKER_BEE,	EVT_LOSER				},
/* ICON_HIVE */			{	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,				EVT_LOSER,				EVT_LOSER				}
};

////////////////////////////////////////////////////////////////////////////
/// HoneyToTheBeeGuts::m_aScatterWinSpinResults
///
/// This array is used to quickly get the SpinResult for the number of
/// icons.  It is indexed by icon count only, since Hives are the only
/// scatter win.  So to get the SpinResult for 5 Hives, use the
/// following code snippet:
///
///		SpinResult result = m_aScatterWinSpinResults[ 5 ];
///
/// In this example result would be set to EVT_FIVE_HIVE.
////////////////////////////////////////////////////////////////////////////
HoneyToTheBeeGuts::SpinResult HoneyToTheBeeGuts::m_aScatterWinSpinResults[6] = 
{
	// ICON_HIVE	   Number of Icons
	EVT_LOSER,		// 0
	EVT_LOSER,		// 1
	EVT_TWO_HIVE,	// 2
	EVT_THREE_HIVE,	// 3
	EVT_FOUR_HIVE,	// 4
	EVT_FIVE_HIVE	// 5
};

////////////////////////////////////////////////////////////////////////////
/// @fn 	 HoneyToTheBeeGuts::HoneyToTheBeeGuts()   
///
/// This is the default constructor for the class.
////////////////////////////////////////////////////////////////////////////
HoneyToTheBeeGuts::HoneyToTheBeeGuts()
{
	// If this doesn't compile it means that someone probably added
	// another icon type but did not update the
	// m_aLineWinSpinResults array. 
	C_ASSERT( countof( m_aLineWinSpinResults ) == NUM_VALID_ICONS );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 HoneyToTheBeeGuts::~HoneyToTheBeeGuts()
///
/// Destructor for HoneyToTheBeeGuts. 
////////////////////////////////////////////////////////////////////////////
HoneyToTheBeeGuts::~HoneyToTheBeeGuts()
{
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 HoneyToTheBeeGuts::SpinResult HoneyToTheBeeGuts::getWorkerBeeLineWinResult( uint8* stops ) const
///
/// @param   stops			The array of stop positions for the line.
///
/// @return	 SpinResult		A payout from the SpinResult enumeration.
///
/// This helper method is used to calculate the Worker Bee line win payout.
////////////////////////////////////////////////////////////////////////////
HoneyToTheBeeGuts::SpinResult HoneyToTheBeeGuts::getWorkerBeeLineWinResult( uint8* stops ) const
{
	// Count how many Worker Bees we have at the beginning of the line.
	for ( uint8 i = 0; i < getReelCount(); i++ )
    {
		// If this icon is not a Worker Bee...
		if ( getIcon( getGameModeId(), i, stops[i] )->iconid != ICON_WORKER_BEE )
		{
			// then we have our total.
			break;
		}
    }

	// Use the array to look up the spin result for i Worker Bees.
	return lookupLineWinSpinResult( ICON_WORKER_BEE, i );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 HoneyToTheBeeGuts::SpinResult HoneyToTheBeeGuts::getLineWinResultWithPossibleSubstitution( uint8* stops, uint8 testIcon, bool& bSubstitution ) const
///
/// @param   stops			The array of stop positions for the line.
/// @param   testIcon		The icon we are looking for, typically the first icon on the line.
/// @param   bSubstitution	An output parameter that is set if a subsitution is used.
///
/// @return	 SpinResult		A payout from the SpinResult enumeration.
///
/// This helper method is used to calculate the line win payout with a possible substitution.
////////////////////////////////////////////////////////////////////////////
HoneyToTheBeeGuts::SpinResult HoneyToTheBeeGuts::getLineWinResultWithPossibleSubstitution(
	uint8* stops, uint8 testIcon, bool& bSubstitution ) const
{
	// Initialize the out parameter
	bSubstitution = false;

	// Loop through the remaining reels until the streak is over
	for ( int i = 1; i < getReelCount(); i++ )
    {
		// See what this icon is.
		uint8 currentIcon = getIcon( getGameModeId(), i, stops[i] )->iconid;

		// If the current icon is a Worker Bee...
		if ( currentIcon == ICON_WORKER_BEE )
		{
			// then use it as a substitution symbol and continue on.
			bSubstitution = true;
			continue;
		}
		// Otherwise if the test icon is a Worker Bee...
		else if ( testIcon == ICON_WORKER_BEE )
		{
			// then we still have a substitution.  In this case the line
			// started out with a Worker Bee.
			bSubstitution = true;

			// We have now found our first icon that is not a Worker Bee.  All
			// of the rest of the icons will have to match this one now for the
			// streak to continue, so change our test icon.
			testIcon = currentIcon;
			continue;
		}

		// If the current icon is not the test icon...
        if ( currentIcon != testIcon )
        {
			// then the streak is over so break out of the loop
			break;
        }
    }

	// If we made it all the way through and the testIcon is still a worker bee...
	if ( testIcon == ICON_WORKER_BEE )
	{
		// then this must be a 5 worker bee win, so reset the substitution flag.
		bSubstitution = false;

		// Actually this should never happen in this game because there aren't 
		// any worker bees on the last reel.
		ASSERT( false );
	}

	// Use the array to look up the spin result for i testIcons.
	return lookupLineWinSpinResult( testIcon, i );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 bool HoneyToTheBeeGuts::doesWorkerBeeLineWinPayMore( SpinResult srWorkerBee, SpinResult srOther, bool bSubstitution ) const
///
/// @param   srWorkerBee	The spin result to calculate the Worker Bee payout for.
/// @param   srOther		The spin result to calculate the other payout for.
/// @param   bSubstitution	Set if we should use the substitution multiplier with the other payout.
///
/// @return	 bool			True if the Worker Bee win is larger.
///
/// This helper method is used to see which payout is larger.
////////////////////////////////////////////////////////////////////////////
bool HoneyToTheBeeGuts::doesWorkerBeeLineWinPayMore( SpinResult srWorkerBee,
	SpinResult srOther, bool bSubstitution ) const
{
	// Lookup the payout for the Worker Bee line win.
	long nWorkerBeeLineWinPayout = getPayout( srWorkerBee )->payoutAmount;

	// See what the total payout for the other line win is without the substitution
	// multiplier (if any).
	long nOtherLineWinPayout = getPayout( srOther )->payoutAmount;

	// If we had a substitution...
	if ( bSubstitution )
	{
		// then factor in the multiplier.
		nOtherLineWinPayout *= MULTIPLIER_SUBSTITUTION;
	}

	// Return true if the Worker Bee payout is bigger.
    return ( nWorkerBeeLineWinPayout > nOtherLineWinPayout );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 HoneyToTheBeeGuts::classifyStops()   
///
/// @param   stops      The array of stop positions for the line.
/// @param   bet        The value of the line bet.
/// @param   line       The index of the line.
///
/// @return	 uint8      A payout from the SpinResult enumeration as a uint8.    
///
/// This helper method is used to calculate the payout on a line.
////////////////////////////////////////////////////////////////////////////
void HoneyToTheBeeGuts::classifyStops( SpinLine* spinline, uint8* stops, uint8 bet, uint8 line,
	uint8& rtnresult, uint8& multiplier )
{
	// Track the Worker Bee result separately since it might be changed to a substitution.
	SpinResult srWorkerBeeLineWinResult = getWorkerBeeLineWinResult( stops );

	// Now see what the payout is if we use any Worker Bees we find as substitution symbols.
	// Start with the icon on the first reel.
	uint8 testIcon = getIcon( getGameModeId(), 0, stops[0] )->iconid;

	// Declare a local variable to track whether or not we found a substitution.
	bool bSubstitution = false;

	// See what the spin result is if we used any Worker Bees as substitution symbols.
	SpinResult srResultWithPossibleSubstitution =
		getLineWinResultWithPossibleSubstitution( stops, testIcon, bSubstitution );

	// Use the larger payout between the regular Worker Bee evaluation compared to
	// the evaluation with the possible substitution.
    if ( doesWorkerBeeLineWinPayMore( srWorkerBeeLineWinResult,
		srResultWithPossibleSubstitution, bSubstitution ) )
	{
		// The Worker Bees by themselves pay out more so use the Worker Bee result.
		rtnresult = srWorkerBeeLineWinResult;
	}
	else
	{
		// Otherwise the result with the substitution (if any) paid more so use it.
		rtnresult = srResultWithPossibleSubstitution;

		// If we did actually have a substitution and a winning spin...
		if ( bSubstitution && srResultWithPossibleSubstitution != EVT_LOSER )
		{
			// then add a note and change the multiplier.
			spinline->addNote( NOTE_SUBSTITUTION );
			multiplier = MULTIPLIER_SUBSTITUTION;
		}
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn		HoneyToTheBeeGuts::tryForJackpot()
///
///	This function tries to win the jackpot and adds the scatter if we hit
/// the jackpot.
////////////////////////////////////////////////////////////////////////////
void HoneyToTheBeeGuts::tryForJackpot( SlotSpin* spin )
{
	// If we are in the first paid spin (NOT a bonus spin)...
	if ( getGameModeId() == MODE_NORMAL ) 
	{
		// then try for the Random Progressive Jackpot
		// If we hit the jackpot...
		if ( getRandomProgressiveJackpotResult( spin->getSpinLineCount() ) || 
             m_bForceRandomJackpot == true )
		{
			// then add a jackpot scatter to the spin.
			spin->addScatter( 0,
				getPayout( EVT_RANDOM_PROGRESSIVE_WINNER )->payoutAmount, 1,
				true, EVT_RANDOM_PROGRESSIVE_WINNER, 0 );
		}
    }
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 HoneyToTheBeeGuts::getScatterWinResult()   
///
/// @param   spin						The current spin object.
/// @param   nScatterPositions			The bit mask for the scatters positions that we find.
/// @param   nFeatureScatterPositions	The bit mask for the feature trigger scatter positions that we find.
///
/// @return	 SpinResult			A payout from the SpinResult enumeration.
///
/// This helper method is used to evaluate the spin for scatters.
////////////////////////////////////////////////////////////////////////////
HoneyToTheBeeGuts::SpinResult HoneyToTheBeeGuts::getScatterWinResult( SlotSpin* spin, int32& nScatterPositions, int32& nFeatureScatterPositions ) const
{
	// Declare a local variable to keep up with the icon count.
	uint8 numScatterIcons = 0;

	// Initialize the scatterPositions.
	nScatterPositions = 0;
	nFeatureScatterPositions = 0;

	// Keep up with whether or not we found a Worker Bee.
	bool bFoundWorkerBee = false;

	// Loop through each reel.
  	for( int reel = 0; reel < getReelCount(); reel++ )
	{
		// The position bit for the current icon in case it needs to be stored in a mask.
		uint8 position = reel;

		// Loop through each row in the wheel house.
		for( int row = 0; row < 3; row++ )
		{
			// Get the current stop.
			uint8 stop = relativeWheelPosition( getGameModeId(), reel,
				spin->getStops()[ reel ], row );

			// Get the icon for the current stop.
			uint8 currentIcon = getIcon( getGameModeId(), reel, stop )->iconid;
			
			// If the icon is the one we are looking for...
			if ( currentIcon == ICON_HIVE )
			{
				// then increase the icon count
				numScatterIcons++;

				// Set the bit in the position mask for this stop.
				nScatterPositions |= 1 << position;

				// If this is the last reel and we found a Worker Bee
				if ( reel == 4 && bFoundWorkerBee )
				{
					// then also set the bit in the feature position mask for this stop.
					nFeatureScatterPositions |= 1 << position;
				}
			}
			// Otherwise look for a Worker Bee for the feature trigger.
			else if ( !bFoundWorkerBee && currentIcon == ICON_WORKER_BEE )
			{
				// then set the bit in the feature position mask for this stop.
				nFeatureScatterPositions |= 1 << position;

				// Set the flag since we only need one worker bee.
				bFoundWorkerBee = true;

				// This should only happen on the first 4 reels.
				ASSERT( reel < 4 );
			}

			// Move to the next bit in the position mask.
            position += 5;
		}
	}

	// Use the array to look up the spin result for numScatterIcons.
	return lookupScatterWinSpinResult( numScatterIcons );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 HoneyToTheBeeGuts::evaluateScatterWin()
///
/// This function determines if the current spin wins a scatter payment and
/// adds the scatter.
////////////////////////////////////////////////////////////////////////////
void HoneyToTheBeeGuts::evaluateScatterWin( SlotSpin* spin, SpinResult srScatterResult, int32 nScatterPositions )
{
	// If we got the scatter win...
	if ( srScatterResult != EVT_LOSER )
	{
		// then initialize our multiplier for scatters.
		int32 multiplier = 1;

		// If we are in free spin mode...
		if ( getGameModeId() == MODE_FREE_SPIN )
		{
			// then use the free spin scatter multiplier.
			multiplier = MULTIPLIER_FREE_SPIN;
		}

		// Get the base payout for the scatter.
		int32 nPayout = getPayout( srScatterResult )->payoutAmount;
		
		// Scatters payout based on the total bet, so factor in the number of bet lines.
		nPayout *= spin->getSpinLineCount();

		// Add the payout (with multiplier) to the total win for this spin.
		increaseTotalWin( nPayout * multiplier );
    
		// Add a scatter to the spin.
		SlotScatter* pScatter = spin->addScatter( nScatterPositions,
			nPayout * getBetPerLine(), multiplier, false, srScatterResult, 0 );

		// If the free spin multiplier was used...
		if ( multiplier == MULTIPLIER_FREE_SPIN )
		{
			// then add the free spin note.
			pScatter->addNote( NOTE_FREE_SPIN );
		}
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 HoneyToTheBeeGuts::evaluateFeatureTrigger()
///
/// This function determines if the current spin triggered the free game
/// feature and adds the appropriate scatter.
////////////////////////////////////////////////////////////////////////////
void HoneyToTheBeeGuts::evaluateFeatureTrigger( SlotSpin* spin, int32 nFeatureScatterPositions )
{
	int32 nWorkerBeeMask = SCATTER_REEL_0_MASK | SCATTER_REEL_1_MASK |
		SCATTER_REEL_2_MASK | SCATTER_REEL_3_MASK;

	// If we found a Worker Bee...
	if ( nFeatureScatterPositions & nWorkerBeeMask )
	{
		// then we will at the very least have a near miss.
		SpinResult srFeatureTrigger = EVT_FEATURE_NEAR_MISS;

		// We need a place to keep up with how many free spins to award.
		int32 nFreeSpins = 0;

		// If there was a Hive on the last reel...
		if ( nFeatureScatterPositions & SCATTER_REEL_4_MASK )
		{
			// then we triggered the feature.
			setGameModeId( MODE_FREE_SPIN );

			// The spin result is not a near miss any more.
			srFeatureTrigger = EVT_FREE_GAME_FEATURE;

			// Award 15 free spins since it actually triggered.
			nFreeSpins = 15;

			// Add the free spins.
			incrementFreeSpins( nFreeSpins );
		}

		// Add a scatter to the spin for the near miss or the feature trigger.
		spin->addScatter( nFeatureScatterPositions, 0, 0, false,
			srFeatureTrigger, nFreeSpins );
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 HoneyToTheBeeGuts::classifyScatters( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This is an override of the framework method.  
/// It determines if the current spin merits free spins or scatter payments.
////////////////////////////////////////////////////////////////////////////
void HoneyToTheBeeGuts::classifyScatters( SlotSpin* spin )
{	
	// Try for the Random Progressive Jackpot
	tryForJackpot( spin );

	// Declare some local variables to track the scatter positions.
	int32 nScatterPositions = 0;
	int32 nFeatureScatterPositions = 0;

	// See if there is a scatter win.
	SpinResult srResult = getScatterWinResult( spin, nScatterPositions, nFeatureScatterPositions );

	// Add the scatter, if any.
	evaluateScatterWin( spin, srResult, nScatterPositions );

	// Add the scatters for the free game feature.
	evaluateFeatureTrigger( spin, nFeatureScatterPositions );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 HoneyToTheBeeGuts::isValidGuts()   
///
/// @return	 bool8      True if gut is valid.    
///
/// This helper method is used to see if the current gut ID is valid.
////////////////////////////////////////////////////////////////////////////
bool8 HoneyToTheBeeGuts::isValidGuts()
{
	// Make sure that the m_aLineWinSpinResults array has an entry for each reel,
	// and an additional entry in case there are 0 icons on the line.
	// Note:  we can't do this in the constructor because we have to call
	// getReelCount.
	ASSERT( countof( m_aLineWinSpinResults[0] ) == getReelCount( MODE_NORMAL ) + 1 );

	// Make sure that the m_aScatterWinSpinResults array has an entry for each reel,
	// and an additional entry in case there are 0 icons on the line.
	ASSERT( countof( m_aScatterWinSpinResults ) == getReelCount( MODE_NORMAL ) + 1 );

	// Now make sure that both game modes have the same number of reels.
	ASSERT( getReelCount( MODE_NORMAL ) == getReelCount( MODE_FREE_SPIN ) );

	// Finally just check to see if the gut is within the right range.
    return ( getGutsId() >= 3300 && getGutsId() < 3400 );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 HoneyToTheBeeGuts::getGameModeMultiplierAndNoteId( uint8& nGameModeMultiplier, uint8& nNoteId )
///
/// @param   nGameModeMultiplier	The multiplier for the game mode.
/// @param   nNoteId				The note ID for the game mode.
///
/// This helper method is used to get the game mode multiplier and note ID.
////////////////////////////////////////////////////////////////////////////
void HoneyToTheBeeGuts::getGameModeMultiplierAndNoteId( uint8& nGameModeMultiplier,
	uint8& nNoteId )
{
	// If we are in free spin mode...
    if ( getGameModeId() == MODE_FREE_SPIN )
    {
		// then return the free spin multiplier...
        nGameModeMultiplier = MULTIPLIER_FREE_SPIN;

		// and the free spin note.
        nNoteId = NOTE_FREE_SPIN;
    }
	// Otherwise...
    else
    {
		// just defer to the base class.
        SlotGuts::getGameModeMultiplierAndNoteId( nGameModeMultiplier, nNoteId );
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//
//	Debugging code
//
////////////////////////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////
/// @fn 	 HoneyToTheBeeGuts::preSpinClassifyResults( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This method is overridden in debug spins mode to alter the reel stops.
////////////////////////////////////////////////////////////////////////////
void HoneyToTheBeeGuts::preSpinClassifyResults( SlotSpin* spin )
{
	SlotGuts::preSpinClassifyResults(spin);
	return;
}